﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SqlServer.Dts.Pipeline;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;

using Microsoft.SqlServer.Dts.Runtime.Wrapper;

namespace DP.Sharepoint.SiteWorkflow.SSIS
{
    /// <summary>
    /// custom SSIS dataflow component which allows starting a SharePoint Site Workflow for each row in the dataflow (think OLE DB Command component)
    /// </summary>
    [DtsPipelineComponentAttribute(DisplayName = "SharePoint Site Workflow Destination",
        ComponentType = ComponentType.DestinationAdapter,
          IconResource = "DP.Sharepoint.SiteWorkflow.SSIS.workflow.ico",
          UITypeName = "DP.Sharepoint.SiteWorkflow.SSIS.SharePointSiteWorkflowDestinationUI, DP.Sharepoint.SiteWorkflow.SSIS, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ead1b02ed990c0f3",
          Description = "Allows to start Sharepoint Site Workflows from Data Flow."
       )]
    public sealed class SharepointSiteWorkflowDestination : PipelineComponent
    {
        public string SiteUrl { get { return ComponentMetaData.CustomPropertyCollection[CustomProperties.SiteUrl].Value as string; } }
        public string WorkflowName { get { return ComponentMetaData.CustomPropertyCollection[CustomProperties.WorkflowName].Value as string; } }
        public string WorkflowParameterMapping { get { return ComponentMetaData.CustomPropertyCollection[CustomProperties.WorkflowParameterMapping].Value as string; } }


        struct ColumnInfo
        {
            public int bufferIndex;
            public string Name;
        }

        List<ColumnInfo> inputIndex = new List<ColumnInfo>();

        public override IDTSOutput100 InsertOutput(DTSInsertPlacement insertPlacement, int outputID)
        {
            throw new ApplicationException("Cannot add outputs to this component!");

        }

        public override void ProvideComponentProperties()
        {
            // Reset the component.
            base.RemoveAllInputsOutputsAndCustomProperties();

            ComponentMetaData.ContactInfo = "http://siteworkflow.codeplex.com/";

            var siteUrl = ComponentMetaData.CustomPropertyCollection.New();

            siteUrl.Name = CustomProperties.SiteUrl;

            siteUrl.ExpressionType = DTSCustomPropertyExpressionType.CPET_NOTIFY;

            var workflowName = ComponentMetaData.CustomPropertyCollection.New();

            workflowName.Name = CustomProperties.WorkflowName;

            workflowName.ExpressionType = DTSCustomPropertyExpressionType.CPET_NOTIFY;

            var workflowParameterMapping = ComponentMetaData.CustomPropertyCollection.New();

            workflowParameterMapping.Name = CustomProperties.WorkflowParameterMapping;

            AddNewInput();

        }

        private void AddNewInput()
        {
            IDTSInput100 input = ComponentMetaData.InputCollection.New();

            input.HasSideEffects = true;

            input.Name = "Start Sharepoint Site Workflow Input";

        }

        public override void OnInputPathAttached(int inputID)
        {
            base.OnInputPathAttached(inputID);

            IDTSInput100 input = ComponentMetaData.InputCollection.GetObjectByID(inputID);

            IDTSVirtualInput100 vInput = input.GetVirtualInput();

            foreach (IDTSVirtualInputColumn100 vCol in vInput.VirtualInputColumnCollection)
            {
                this.SetUsageType(inputID, vInput, vCol.LineageID, DTSUsageType.UT_READONLY);
            }

        }

        private void FireError(string error)
        {
            bool cancel;

            ComponentMetaData.FireError(0, ComponentMetaData.Name, error, string.Empty, 0, out cancel);

        }

        private void FireWarning(string message)
        {

            ComponentMetaData.FireWarning(0, ComponentMetaData.Name, message, string.Empty, 0);

        }

        public override void PreExecute()
        {
            base.PreExecute();
            //build up index of input columns to be used in ProcessInput() for referencingcolumns
            IDTSInput100 input = ComponentMetaData.InputCollection[0];

            for (int col = 0; col < input.InputColumnCollection.Count; col++)
            {
                ColumnInfo colInfo = new ColumnInfo();

                colInfo.bufferIndex = BufferManager.FindColumnByLineageID(input.Buffer, input.InputColumnCollection[col].LineageID);

                colInfo.Name = input.InputColumnCollection[col].Name;

                inputIndex.Add(colInfo);

            }
        }

        public override DTSValidationStatus Validate()
        {

            if (string.IsNullOrEmpty(SiteUrl) || !Uri.IsWellFormedUriString(SiteUrl, UriKind.Absolute))
            {
                FireError("Must Provide valid SiteUrl");

                return DTSValidationStatus.VS_ISBROKEN;
            }

            if (string.IsNullOrEmpty(WorkflowName))
            {
                FireError("Must Provide WorkflowName");

                return DTSValidationStatus.VS_ISBROKEN;
            }

            if (string.IsNullOrEmpty(WorkflowParameterMapping))
            {
                FireError("Must Provide WorkflowParameterMapping");

                return DTSValidationStatus.VS_ISBROKEN;
            }

            WorkflowParameterMappingManger mgr = null;

            try
            {
                mgr = new WorkflowParameterMappingManger(WorkflowParameterMapping);

                if (ComponentMetaData.InputCollection.Count > 0)
                {
                    foreach (var item in mgr.GetMappings())
                    {

                        try
                        {
                            var col = ComponentMetaData.InputCollection[0].InputColumnCollection[item.Key];
                        }
                        catch
                        {

                            FireWarning(string.Format("Column '{1}' bound to workflow parameter '{0}' is no longer present in dataflow.", item.Value, item.Key));
                        }
                    }
                }

            }
            catch (Exception e)
            {
                FireError("WorkflowParameterMapping is not valid!: " + e.Message);

                return DTSValidationStatus.VS_ISBROKEN;
            }



            if (ComponentMetaData.ValidateExternalMetadata)
            {
                try
                {
                    var assistant = new DeclarativeSiteWorkflowAssistant(new Uri(SiteUrl));

                    var info = assistant.GetDeclarativeSiteWorkflowInfo(WorkflowName);

                    foreach (var item in mgr.GetMappings())
                    {
                        if (info.WorkflowParameters.Where(x => x.Name == item.Value).FirstOrDefault() == null)
                            FireWarning(string.Format("Workflow parameter '{0}' bound to column '{1}' is no longer present on in the '{2}' workflow.", item.Value, item.Key, info.WorkflowTitle));

                    }

                }
                catch (Exception e)
                {
                    FireError("Error while validating Sharepoint Site Workflow: " + e.ToString());

                    return DTSValidationStatus.VS_ISBROKEN;
                }
            }

            if (!ComponentMetaData.AreInputColumnsValid)
            {
                return DTSValidationStatus.VS_NEEDSNEWMETADATA;
            }

            return base.Validate();
        }

        public override void ProcessInput(int inputID, PipelineBuffer buffer)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }


            var paramManager = new WorkflowParameterMappingManger(WorkflowParameterMapping);

            var siteWkflowAssistant = new DeclarativeSiteWorkflowAssistant(new Uri(SiteUrl));

            var wkInfo = siteWkflowAssistant.GetDeclarativeSiteWorkflowInfo(WorkflowName);

            var paramsMapping = paramManager.GetMappingsDictionary();


            if (!buffer.EndOfRowset)
            {
                while (buffer.NextRow() == true) // foreach row in dataflow
                {
                    for (int x = 0; x < inputIndex.Count; x++)
                    {
                        ColumnInfo ci = (ColumnInfo)inputIndex[x];

                        if (!buffer.IsNull(ci.bufferIndex))
                        {
                            if (paramsMapping.ContainsKey(ci.Name))
                            {
                                var workflowParamName = paramsMapping[ci.Name];

                                var val = buffer[ci.bufferIndex].ToString();

                                wkInfo.WorkflowParameters.Where(p => p.Name == workflowParamName).First().Value = val;

                            }


                        }
                    }
                    try
                    {
                        siteWkflowAssistant.StartDeclarativeSiteWorkflow(wkInfo);
                    }
                    catch (Exception ex)
                    {
                        this.FireError("Error while attempting to talk to Site Workflow Activation Webservice: " + ex);

                        throw;

                    }
                }


            }
        }

        public static Type GetManagedType(DataType dtsDataType)
        {
            return BufferTypeToDataRecordType(dtsDataType);

        }

    }

    internal static class CustomProperties
    {
        public static readonly string SiteUrl = "SiteUrl";
        public static readonly string WorkflowName = "WorkflowName";
        public static readonly string WorkflowParameterMapping = "WorkflowParameterMapping";


    }
}
